package com.fy.paper.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fy.core.consts.ExamContant;
import com.fy.core.consts.PaperConst;
import com.fy.core.consts.QuTypeConstant;
import com.fy.core.context.AuthContext;
import com.fy.core.enums.ApiErrorEnum;
import com.fy.core.exception.ServiceException;
import com.fy.core.vo.ApiRest;
import com.fy.exam.entity.Exam;
import com.fy.exam.entity.ExamRepo;
import com.fy.exam.mapper.ExamMapper;
import com.fy.exam.mapper.ExamRepoMapper;
import com.fy.paper.dto.FillAnswerDto;
import com.fy.paper.entity.Paper;
import com.fy.paper.entity.PaperQu;
import com.fy.paper.entity.PaperQuAnswer;
import com.fy.paper.mapper.PaperMapper;
import com.fy.paper.mapper.PaperQuAnswerMapper;
import com.fy.paper.mapper.PaperQuMapper;
import com.fy.paper.service.PaperService;
import com.fy.paper.vo.DtkVo;
import com.fy.paper.vo.PaperQuAnswerVo;
import com.fy.paper.vo.PaperQuDetail;
import com.fy.qu.entity.Qu;
import com.fy.qu.entity.QuAnswer;
import com.fy.qu.mapper.QuAnswerMapper;
import com.fy.qu.mapper.QuMapper;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 试卷 服务实现类
 * </p>
 *
 * @author
 * @since 2023-06-16
 */
@Service
public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements PaperService {

    @Autowired
    private ExamMapper examMapper;
    @Autowired
    private ExamRepoMapper examRepoMapper;
    @Autowired
    private QuMapper quMapper;

    @Autowired
    private PaperQuMapper paperQuMapper;
    @Autowired
    private QuAnswerMapper quAnswerMapper;
    @Autowired
    private PaperQuAnswerMapper paperQuAnswerMapper;

    private static List<String> ABC = Arrays.asList(new String[]{
            "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K","L","M","N","O","P","Q","R","S","T","U","V","W","X"
            ,"Y","Z"
    });


    @Override
    @Transactional
    public ApiRest<String> create(String examId) {

        //1：判断试卷是否存在
        LambdaQueryChainWrapper<Exam> examLambdaQueryChainWrapper =new LambdaQueryChainWrapper<>(examMapper);
        Exam exam = examLambdaQueryChainWrapper.eq(Exam::getId, examId).one();
        if (exam == null) {
            throw new ServiceException(ApiErrorEnum.EXAM_NOT_EXIT);
        }

        //2：判断是否在考试区间
        if (exam.getTimeLimit() == ExamContant.EXAM_IS_TIMELIMIT){
            //当前系统时间必须得在考试开始时间和结束时间区间内，否则不准考试
            Date now =new Date();
            Date startTime = exam.getStartTime();
            Date endTime = exam.getEndTime();
            //DateUtil.compare 里两个时间 c1=c2 return 0,c1>c2 1,c1<c2 -1
            if(DateUtil.compare(now,startTime) == -1){
                throw new ServiceException(ApiErrorEnum.EXAM_NOT_IN_EXAMTIME);
            }
            if(DateUtil.compare(now,endTime) == 1){
                throw new ServiceException(ApiErrorEnum.EXAM_NOT_IN_EXAMTIME);
            }
        }

        //3：判断考试是否结束
        if(exam.getState()== ExamContant.CLOSE){
            throw new ServiceException(ApiErrorEnum.EXAM_CLOSED);
        }
        String userId = AuthContext.getUser().getUserId()+"";

        //4：判断用户是否已经有了该考试的试卷
        LambdaQueryChainWrapper<Paper> paperLambdaQueryChainWrapper =new LambdaQueryChainWrapper<>(this.baseMapper);
        Paper paper = paperLambdaQueryChainWrapper.eq(Paper::getExamId, examId)
                .eq(Paper::getUserId, userId).one();


        if(paper.getState() == PaperConst.END){
            throw new ServiceException(ApiErrorEnum.PAPER_IS_CLOSED);
        }

        if (paper != null) {//用户已经发过该考试的试卷
            return ApiRest.success(paper.getId());
        }

        //5：生成空白试卷，保存试卷基本信息
        Paper newPaper =new Paper();
        newPaper.setId(IdWorker.getIdStr());
        newPaper.setExamId(examId);
        newPaper.setUserId(userId);
        newPaper.setDepartId(AuthContext.getUser().getDeptId()+"");
        newPaper.setCreateTime(new Date());
        newPaper.setUpdateTime(new Date());

        Calendar calendar =Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.MINUTE,exam.getTotalTime());
        newPaper.setLimitTime(calendar.getTime());//试卷的截止时间=系统当前时间+考试规定的时长

        newPaper.setState(PaperConst.ING);
        newPaper.setTitle(exam.getTitle());
        newPaper.setUserScore(0);
        newPaper.setUserTime(0);//用户时长
        newPaper.setTotalTime(exam.getTotalTime());//考试时长
        //总分
        newPaper.setTotalScore(exam.getTotalScore());
        //主观题总分
        Map map = examMapper.totalSubScore(examId);
        BigDecimal subScore = (BigDecimal) map.get("sub_score");
        newPaper.setSubjScore(subScore.intValue());
        //客观题总分
        newPaper.setObjScore(exam.getTotalScore()-subScore.intValue());
        newPaper.setHasSaq(subScore.intValue()>0?1:0);//是否有简答题 0:没有 1:有
        this.baseMapper.insert(newPaper);


        //6：组卷 参考exam_repo 每个题库选择哪几道题
        List<Qu> paperQus = zujuan(examId);
        List<PaperQu> paperQuList = paperQus.stream().map(item -> {
            PaperQu paperQu = new PaperQu();
            paperQu.setId(IdWorker.getIdStr());
            paperQu.setPaperId(newPaper.getId());
            paperQu.setQuId(item.getId());
            paperQu.setAnswer(""); //主观题用户的输入答案
            paperQu.setAnswered(0);//0：未答 1：已经作答
            paperQu.setActualScore(0);
            paperQu.setIsRight(0);
            paperQu.setQuType(item.getQuType());
            paperQu.setScore(item.getScore());//题目分数

            return paperQu;
        }).collect(Collectors.toList());

        //7：生成题号
        int sort = 0;
        for (int i = 0; i < paperQuList.size(); i++) {
            if(paperQuList.get(i).getQuType() == QuTypeConstant.RADIO){//单选题
                paperQuList.get(i).setSort(++sort);
            }
        }

        for (int i = 0; i < paperQuList.size(); i++) {
            if(paperQuList.get(i).getQuType() == QuTypeConstant.MULTI){//多选题
                paperQuList.get(i).setSort(++sort);
            }
        }

        for (int i = 0; i < paperQuList.size(); i++) {
            if(paperQuList.get(i).getQuType() == QuTypeConstant.JUDGE){//判断题
                paperQuList.get(i).setSort(++sort);
            }
        }

        for (int i = 0; i < paperQuList.size(); i++) {
            if(paperQuList.get(i).getQuType() == QuTypeConstant.JDT){//简答题
                paperQuList.get(i).setSort(++sort);
            }
        }
        //插入表
        paperQuList.forEach(item->{
            paperQuMapper.insert(item);
        });

        //8：给每道题绑定答案选项
        for (PaperQu paperQu : paperQuList) {
            String quId = paperQu.getQuId();
            //查询题目的答案选项
            QueryWrapper<QuAnswer> queryWrapper =new QueryWrapper<>();
            queryWrapper.lambda().eq(QuAnswer::getQuId,quId);
            List<QuAnswer> quAnswers = quAnswerMapper.selectList(queryWrapper);
            for (int i = 0; i < quAnswers.size(); i++) {
                PaperQuAnswer paperQuAnswer =new PaperQuAnswer();
                paperQuAnswer.setId(IdWorker.getIdStr());
                paperQuAnswer.setPaperId(newPaper.getId());
                paperQuAnswer.setQuId(quId);
                paperQuAnswer.setAnswerId(quAnswers.get(i).getId());
                paperQuAnswer.setIsRight(quAnswers.get(i).getIsRight());
                paperQuAnswer.setChecked(0);//0：未选 1：选
                paperQuAnswer.setAbc(ABC.get(i));
                paperQuAnswerMapper.insert(paperQuAnswer);
            }
        }


        return ApiRest.success(newPaper.getId());
    }

    @Override
    public ApiRest<DtkVo> dtk(String paperId) {
        //判断有没有这张试卷
        Paper paper = this.baseMapper.selectById(paperId);
        if (paper == null) {
            throw new ServiceException(ApiErrorEnum.PAPER_NOT_EXIT);
        }
        //判断试卷状态（是否结束）
        if(paper.getState() == PaperConst.END){
            throw new ServiceException(ApiErrorEnum.PAPER_IS_CLOSED);
        }

        //查询出该试卷单选题,多选题,判断题，简答题
        QueryWrapper<PaperQu> radioQuery = new QueryWrapper<>();
        radioQuery.lambda().eq(PaperQu::getPaperId,paperId)
                .eq(PaperQu::getQuType,QuTypeConstant.RADIO)
                .orderByAsc(PaperQu::getSort);
        List<PaperQu> radios = paperQuMapper.selectList(radioQuery);

        QueryWrapper<PaperQu> multiQuery = new QueryWrapper<>();
        multiQuery.lambda().eq(PaperQu::getPaperId,paperId)
                .eq(PaperQu::getQuType, QuTypeConstant.MULTI)
                .orderByAsc(PaperQu::getSort);
        List<PaperQu> multis = paperQuMapper.selectList(multiQuery);


        QueryWrapper<PaperQu> judgeQuery = new QueryWrapper<>();
        judgeQuery.lambda().eq(PaperQu::getPaperId,paperId)
                .eq(PaperQu::getQuType, QuTypeConstant.JUDGE)
                .orderByAsc(PaperQu::getSort);
        List<PaperQu> judges = paperQuMapper.selectList(judgeQuery);


        QueryWrapper<PaperQu> jdtQuery = new QueryWrapper<>();
        jdtQuery.lambda().eq(PaperQu::getPaperId,paperId)
                .eq(PaperQu::getQuType, QuTypeConstant.JDT)
                .orderByAsc(PaperQu::getSort);
        List<PaperQu> jdts = paperQuMapper.selectList(jdtQuery);

        DtkVo dtkVo =new DtkVo();
        dtkVo.setRadios(radios);
        dtkVo.setMultis(multis);
        dtkVo.setJudges(judges);
        dtkVo.setJdts(jdts);

        //计算剩余时间 = 试卷结束时间 - 系统时间
        Date limitTime = paper.getLimitTime();
        long endTime = limitTime.getTime();
        Date now = new Date();
        dtkVo.setLeftSeconds((endTime-now.getTime())/1000);

        return ApiRest.success(dtkVo);


    }

    @Override

    public ApiRest<PaperQuDetail> paperQuDetail(String paperId, String quId) {
        //判断有没有这张试卷
        Paper paper = this.baseMapper.selectById(paperId);
        if (paper == null) {
            throw new ServiceException(ApiErrorEnum.PAPER_NOT_EXIT);
        }
        //判断试卷状态（是否结束）
        if(paper.getState() == PaperConst.END){
            throw new ServiceException(ApiErrorEnum.PAPER_IS_CLOSED);
        }

        QueryWrapper<PaperQu> queryWrapper =new QueryWrapper<>();
        queryWrapper.lambda().eq(PaperQu::getPaperId,paperId)
                .eq(PaperQu::getQuId,quId);
        PaperQu paperQu = paperQuMapper.selectOne(queryWrapper);
        if (paperQu == null) {
            throw new ServiceException(ApiErrorEnum.PAPER_NOT_QU);
        }

        Qu qu = quMapper.selectById(quId);


        PaperQuDetail detail =new PaperQuDetail();
        detail.setSort(paperQu.getSort());
        detail.setQuType(paperQu.getQuType());
        detail.setContent(qu.getContent());

        //答案选项
        QueryWrapper<PaperQuAnswer> paperQuAnswerQueryWrapper =new QueryWrapper<>();
        paperQuAnswerQueryWrapper.lambda().eq(PaperQuAnswer::getPaperId,paperId)
                .eq(PaperQuAnswer::getQuId,quId);
        List<PaperQuAnswer> paperQuAnswers =
                paperQuAnswerMapper.selectList(paperQuAnswerQueryWrapper);

        //PaperQuAnswer --->  PaperQuAnswerVo
        List<PaperQuAnswerVo> paperQuAnswerVoList = paperQuAnswers.stream().map(item -> {
            PaperQuAnswerVo paperQuAnswerVo = new PaperQuAnswerVo();
            paperQuAnswerVo.setAnswerId(item.getAnswerId());

            QuAnswer quAnswer = quAnswerMapper.selectById(item.getAnswerId());
            paperQuAnswerVo.setContent(quAnswer.getContent());

            paperQuAnswerVo.setAbc(item.getAbc());
            paperQuAnswerVo.setChecked(item.getChecked());
            paperQuAnswerVo.setImage(quAnswer.getImage());

            return paperQuAnswerVo;
        }).collect(Collectors.toList());

        detail.setAnswers(paperQuAnswerVoList);


        return ApiRest.success(detail);
    }

    @Override
    @Transactional
    public ApiRest fillAnswer(FillAnswerDto fillAnswerDto) {
        //判断有没有这张试卷
        Paper paper = this.baseMapper.selectById(fillAnswerDto.getPaperId());
        if (paper == null) {
            throw new ServiceException(ApiErrorEnum.PAPER_NOT_EXIT);
        }
        //判断试卷状态（是否结束）
        if(paper.getState() == PaperConst.END){
            throw new ServiceException(ApiErrorEnum.PAPER_IS_CLOSED);
        }


        Qu qu = quMapper.selectById(fillAnswerDto.getQuId());

        LambdaQueryChainWrapper<PaperQu> paperQuLambdaQueryChainWrapper
                =new LambdaQueryChainWrapper<>(paperQuMapper);
        PaperQu paperQu = paperQuLambdaQueryChainWrapper.eq(PaperQu::getPaperId, fillAnswerDto.getPaperId())
                .eq(PaperQu::getQuId, fillAnswerDto.getQuId()).one();


        if(qu.getQuType() == QuTypeConstant.JDT){//简答题
            //维护paper_qu表即可
            LambdaUpdateChainWrapper<PaperQu> paperQuLambdaUpdateChainWrapper
                    = new LambdaUpdateChainWrapper<>(paperQuMapper);
            paperQuLambdaUpdateChainWrapper.eq(PaperQu::getPaperId,fillAnswerDto.getPaperId())
                    .eq(PaperQu::getQuId,fillAnswerDto.getQuId())
                    .set(PaperQu::getAnswered,1)
                    .set(PaperQu::getAnswer,fillAnswerDto.getAnswer())
                    .update();
        }else {//选择题和判断题
            //维护paper_qu_answer表
            //先将所有答案设置为未选择状态，不设置的话，用户点击一个选项就将checked设为1，那瞎几把全点一遍不就都checked了，小bug
            LambdaUpdateChainWrapper<PaperQuAnswer> update0
                    =new LambdaUpdateChainWrapper<>(paperQuAnswerMapper);
            update0.eq(PaperQuAnswer::getPaperId,fillAnswerDto.getPaperId())
                    .eq(PaperQuAnswer::getQuId,fillAnswerDto.getQuId())
                    .set(PaperQuAnswer::getChecked,0)
                    .update();

            for (String answerId : fillAnswerDto.getAnswerIds()) {
                LambdaUpdateChainWrapper<PaperQuAnswer> update1
                        =new LambdaUpdateChainWrapper<>(paperQuAnswerMapper);
                update0.eq(PaperQuAnswer::getPaperId,fillAnswerDto.getPaperId())
                        .eq(PaperQuAnswer::getQuId,fillAnswerDto.getQuId())
                        .eq(PaperQuAnswer::getAnswerId,answerId)
                        .set(PaperQuAnswer::getChecked,1)
                        .update();
            }

            //客观题得分
            List<PaperQuAnswer> paperQuAnswers = this.baseMapper.fillAnswerIsRight(fillAnswerDto.getPaperId(), fillAnswerDto.getQuId());
            if(CollectionUtil.isEmpty(paperQuAnswers)){ //答对
                LambdaUpdateChainWrapper<PaperQu> paperQuLambdaUpdateChainWrapper
                        = new LambdaUpdateChainWrapper<>(paperQuMapper);
                paperQuLambdaUpdateChainWrapper.eq(PaperQu::getPaperId,fillAnswerDto.getPaperId())
                        .eq(PaperQu::getQuId,fillAnswerDto.getQuId())
                        .set(PaperQu::getAnswered,1)
                        .set(PaperQu::getActualScore,paperQu.getScore())
                        .set(PaperQu::getIsRight,1)
                        .update();
            }else{//答错
                LambdaUpdateChainWrapper<PaperQu> paperQuLambdaUpdateChainWrapper
                        = new LambdaUpdateChainWrapper<>(paperQuMapper);
                paperQuLambdaUpdateChainWrapper.eq(PaperQu::getPaperId,fillAnswerDto.getPaperId())
                        .eq(PaperQu::getQuId,fillAnswerDto.getQuId())
                        .set(PaperQu::getAnswered,1)
                        .set(PaperQu::getActualScore,0)
                        .set(PaperQu::getIsRight,0)
                        .update();
            }


        }


        return ApiRest.success();
    }

    @Override
    @Transactional
    public ApiRest handlePaper(String paperId) {
        //判断有没有这张试卷
        Paper paper = this.baseMapper.selectById(paperId);
        if (paper == null) {
            throw new ServiceException(ApiErrorEnum.PAPER_NOT_EXIT);
        }
        //判断试卷状态（是否结束）
        if(paper.getState() == PaperConst.END){
            throw new ServiceException(ApiErrorEnum.PAPER_IS_CLOSED);
        }

        paper.setState(PaperConst.END);
        //用户耗时 = 系统当前时间  - 试卷的创建时间
        long costTime = (new Date().getTime() - paper.getCreateTime().getTime())/1000;
        paper.setUserTime(Integer.parseInt(costTime+"")/60);

        //计算用户得分
        Map map = this.baseMapper.totalObjScoreWhenHandlePaper(paperId);
        BigDecimal obj_score = (BigDecimal) map.get("obj_score");

        paper.setUserScore(obj_score.intValue());
        paper.setUpdateTime(new Date());

        this.baseMapper.updateById(paper);

        return ApiRest.success();
    }

    @NotNull
    private List<Qu> zujuan(String examId){
        //6：组卷 参考exam_repo 每个题库选择哪几道题
        QueryWrapper<ExamRepo> queryWrapper =new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamRepo::getExamId,examId);
        //获取到这场考试选择了哪些题库的哪种题以及数量
        List<ExamRepo> examRepos = examRepoMapper.selectList(queryWrapper);

        List<Qu> excludes =new ArrayList<>();//避免题目重复
        List<Qu> paperQus =new ArrayList<>();//试卷的题目集合
        int totalCount =0;

        for (ExamRepo examRepo : examRepos) {//单选题
            if (examRepo.getRadioCount() > 0) {
                totalCount = totalCount + examRepo.getRadioCount();
                //随机出题
                List<Qu> radios = quMapper.randQu(examRepo.getRepoId(), QuTypeConstant.RADIO, examRepo.getRadioCount(), excludes);
                for (Qu radio : radios) {
                    radio.setScore(examRepo.getRadioScore());
                }
                CollectionUtil.addAll(paperQus, radios);
                CollectionUtil.addAll(excludes, radios);
            }
            if (examRepo.getMultiCount() > 0) {//多选题
                totalCount = totalCount + examRepo.getMultiCount();
                List<Qu> multis = quMapper.randQu(examRepo.getRepoId(), QuTypeConstant.MULTI, examRepo.getMultiCount(), excludes);
                for (Qu multi : multis) {
                    multi.setScore( examRepo.getMultiScore());
                }
                CollectionUtil.addAll(paperQus, multis);
                CollectionUtil.addAll(excludes, multis);
            }
            if (examRepo.getJudgeCount() > 0) {//判断题
                totalCount = totalCount + examRepo.getJudgeCount();
                List<Qu> judges = quMapper.randQu(examRepo.getRepoId(), QuTypeConstant.JUDGE, examRepo.getJudgeCount(), excludes);
                for (Qu judge : judges) {
                    judge.setScore(examRepo.getJudgeScore());
                }
                CollectionUtil.addAll(paperQus, judges);
                CollectionUtil.addAll(excludes, judges);
            }
            if (examRepo.getJdtCount() > 0) {//简答题
                totalCount = totalCount + examRepo.getJdtCount();
                List<Qu> jdts = quMapper.randQu(examRepo.getRepoId(), QuTypeConstant.JDT, examRepo.getJdtCount(), excludes);
                for (Qu jdt : jdts) {
                    jdt.setScore(examRepo.getJdtScore());
                }
                CollectionUtil.addAll(paperQus, jdts);
                CollectionUtil.addAll(excludes, jdts);
            }

            if (paperQus.size() < totalCount) {
                throw new ServiceException(ApiErrorEnum.PAPER_FAIL);
            }
        }

        return paperQus;
    }
}
